/**
 * echarts工具类
 */
// ————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
import * as echarts from "echarts"; // echarts引入区

// 获取引入的渲染列表
let render;

// 导出函数区
export default class Tool {
  // 成员变量
  // ---------------------------------------------------------------------------------------------
  #dom; // 需要嵌入的dom节点
  myChart; // echarts渲染的dom
  classState = true; // 本类的错误统计状态，用于防止后续因为错误的构造，在调用方法造成错误
  option = {}; // 渲染的内容对象
  defIndex = -2; // 默认选中的成员位置

  /**
   * @构造函数
   *
   * @id 绑定展示盒子的id
   * @renderName render列表的筛选名称
   * @defIndex 饼状图的默认展示位置
   * @select 渲染成员列表筛选值
   * @width 初始化改变宽度
   * @height 初始化改变高度
   */
  // ---------------------------------------------------------------------------------------------
  constructor(id = "", renderName, defIndex, select = 0, width, height) {
    // 判断传入id是否存在+此dom节点是否可用
    if (
      this.isError([
        { type: "isId", parameter: id },
        { type: "isDom", parameter: document.getElementById(id) },
      ])
    ) {
      //如果dom可用则带入id赋予this.#dom
      this.#dom = document.getElementById(id);

      // 动态获取当前渲染的值，并赋予给render
      // ======================================================================
      render = require("../render").chart[renderName];

      // 判断是否存在高度和宽度
      // ======================================================================
      const isHeight = height !== null && height !== undefined;
      const isWidth = width !== null && width !== undefined;
      const isDefIndex = defIndex !== null && defIndex !== undefined;

      // 进行批量错误检测
      // ======================================================================
      // 批量检测数组
      let errLIst = [
        { type: "isRender", parameter: render },
        { type: "isSelect", parameter: select },
        { type: "isRenderItem", parameter: render[select] },
      ];

      // 根据宽高来判断是否要添加对应的尺寸判断数据
      isHeight && errLIst.push({ type: "isSize", parameter: height });
      isWidth && errLIst.push({ type: "isSize", parameter: width });
      isDefIndex && errLIst.push({ type: "isIndex", parameter: defIndex });

      // 进行错误验证
      this.isError(errLIst);

      // 只有当全部初始化内容验证通过时才能进行赋值和绑定的操作
      // ======================================================================
      if (this.classState) {
        // 进行绑定渲染对象
        this.option = render[select];
        isDefIndex && (this.defIndex = defIndex);

        // 绑定echarts的盒子，此时判断是否有静态的高和宽传入，有的话则进行绑定
        this.myChart = echarts.init(this.#dom, null, {
          width: isWidth ? width : null,
          height: isHeight ? height : null,
        });
      }
    }
  }

  /**
   * @改变尺寸函数
   *
   * @size 传入指定尺寸的对象，具体参数如下：
   * width 若需要指定宽度则传入，否则不用传入，且为数值类型
   * height 若需要指定高度则传入，否则不用传入，且为数值类型
   *
   */
  // ---------------------------------------------------------------------------------------------
  changeSize = (size) => {
    // 如果没有验证成功则不能执行此函数
    if (!this.classState) {
      return;
    }

    // 如果传入了size参数调用错误工具函数验证传入的size
    size && this.isError({ type: "isResize", parameter: size });

    // 只有当合法值存在 并且 验证是通过了；才能执行尺寸改变方法
    this.classState &&
      (size ? this.myChart.resize(size) : this.myChart.resize());
  };

  /**
   * @错误工具函数
   *
   * @inquire 传入对象或数组类型，进行执行不同的判断，每个成员对象组成如下~~
   *
   * type 传入类型字符串，支持类型为：
   * isId 判断id是否存在
   * isDom 判断dom是否存在
   * isRender 判断渲染数据是否合法并有值
   * isRenderItem 判断查询的渲染成员是否为合法并是对应的对象
   * isData 判断赋值的data参数是否为合法并是对应的数组
   * isSelect 判断筛选渲染成员位置是否为合法并是对应的数值
   * isSize 判断宽高尺寸是否为合法并是对应的数值
   * isIndex 判断饼状图中默认位置信息是否为合法并是对应的数值
   * isResize 判断传入的改变尺寸对象是否合法
   *
   * parameter 查询后续参数，与type值进行匹配查询，对应关系如下：
   * type = isId 时 携带为对应id的字符串
   * type = isDom 时 携带为对应获取的dom节点
   * type = isRender 时 携带为对应的渲染数组
   * type = isRenderItem 时 携带为查询的渲染对象
   * type = isData 时 携带为查询的渲染数组
   * type = isSelect 时 携带为查询的渲染数值
   * type = isSize 时 携带为查询的渲染数值
   * type = isIndex 时 携带为查询的渲染数值
   * type = isResize 时 携带为对象
   *
   * @return 返回一个布尔值，并会改变 classState 的状态，同步当前检测的内容，通过 true，失败 false
   *
   * 注意：
   *  1、若是想批量查询时，inquire传入为 当次查询的对象组成的的数组形式
   *  2、在批量查询时，只有当所有查询成员都通过了，返回结果才是true，否则为false
   */
  // ---------------------------------------------------------------------------------------------
  isError = (inquire = {}) => {
    // 获取传入成员的真实的类型
    const inquireType = Object.prototype.toString
      .call(inquire)
      .match(/^\[object\s(.*)\]$/)[1];

    // 根据传入的类型进行判断执行什么类型的查询判断
    // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    switch (inquireType) {
      case "Object":
        // 对象时，则直接带入参数查询
        this.classState = this.#isErrorItem(inquire.type, inquire.parameter);
        break;

      case "Array":
        // 数组时则进行循环every进行判断查询
        this.classState = inquire.every((value) =>
          this.#isErrorItem(value.type, value.parameter)
        );
        break;

      default:
        // 都不是，则直接进行错误赋予，且报错提醒
        this.classState = false;
        console.error(
          `inquire类型应该为Object或Array，不支持当前传入的inquire类型，请重新传入！当前inquire类型为${inquireType}`
        );
        break;
    }

    // 弹出最终检查的结果
    // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    return this.classState;
  };

  /**
   * @错误函数parameter参数类型查询字典
   * 根据传入type值，查询对应 parameter 应该的大方向类型
   */
  // ======================================================================
  #parameterType = {
    isId: "String",
    isDom: "HTMLDocument",
    isRender: "Array",
    isRenderItem: "Object",
    isData: "Array",
    isSelect: "Number",
    isSize: "Number",
    isIndex: "Number",
    isResize: "Object",
  };

  /**
   * @错误函数配套方法
   * @type 同父级方法，但其使用只能是单一的字符串
   * @parameter 同父级方法
   * @return 返回一个布尔值，通过 true，失败 false
   */
  // ======================================================================
  #isErrorItem = (type = "", parameter) => {
    // 首先进行获取传入的type类型判断
    // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    const ifType = typeof type;

    // 如果不是字符串，则报错
    if (ifType !== "string") {
      console.error(
        `当前传入的type并不是字符串(string)类型，请检查后重新传入，当前传入type类型为${ifType}`
      );
      return false;
    }

    // 然后对type是否属于已有查询的参数
    // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    // 将此时的 parameter参数类型查询字典 的kye进行数组化;
    const isType = Object.keys(this.#parameterType);

    // 如果不属于则报错
    if (!isType.includes(type)) {
      console.error(
        `当前传入的type并不是合法值，当前合法值有:${isType.toString()}，当前type值为${type}`
      );
      return false;
    }

    // 之后判断 parameter 是否传入
    // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    if (parameter === null || parameter === undefined) {
      console.error(`parameter判断参数不能为空！`);
      return false;
    }

    // 最后判断 parameter 是否是对应需要传入的类型
    // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    const parameterType = Object.prototype.toString
      .call(parameter)
      .match(/^\[object\s(.*)\]$/)[1];

    //将此时type的值带入 parameter参数类型查询字典，如果查询的参数不符合规则，则进行报错处理
    if (
      parameterType !== this.#parameterType[type] &&
      !parameterType.includes("HTML")
    ) {
      console.error(
        `当前传入的parameter参数，并不满足type为${type}时的类型，此时parameter参数类型须为${
          this.#parameterType[type]
        }，当前parameter参数类型是${parameterType}`
      );

      // 直接判断失败;
      return false;
    }

    // 都通过时，进行正常的声明判断
    let result = false; // 声明检查结果变量

    // 进行对应的类型判断
    // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    switch (type) {
      case "isId":
        // 进行id是否为空验证时
        result = !!parameter;
        !result && console.error(`id不能为空值，请检查后重新传入！`);
        break;

      case "isDom":
        // 进行dom是否存在验证时
        result = !!parameter;
        !result && console.error(`承载的dom节点不存在，请检查id是否输入正确`);
        break;

      case "isRender":
        // 进行渲染数据数组是否为空验证时
        result = parameter.length !== 0;
        !result &&
          console.error(`图表渲染列表render至少应该有一个成员！请重新设置！`);
        break;

      case "isRenderItem":
        // 如果是对render渲染成员是否存在有效值进行验证时
        result = Object.keys(parameter).length !== 0;
        !result &&
          console.error(`当前渲染的对象至少应该存在一个有效的成员！请注意检查`);
        break;

      case "isData":
        // 如果是对传入的数据进行验证，因为前面已经做了类型判断处理，所以到这里必然是通过的
        // 此处是运行用户传入空列表的内容
        result = true;
        break;

      case "isSelect":
        // 对传入的 select 参数进行验证时
        result = parameter >= 0;
        !result &&
          console.error(
            `传入的render查询位置属性select不能小于0，请进行修改，此时传入的值为${parameter}`
          );
        break;

      case "isSize":
        // 对传入的尺寸参数进行验证时
        result = parameter >= 0;
        !result && console.error(`传入的宽或高尺寸不能小于0，请进行检查修改！`);
        break;

      case "isIndex":
        // 对传入的位置信息参数进行验证时
        result = parameter >= -1;
        !result && console.error(`传入的默认位置不能小于-1，请进行检查修改！`);
        break;

      case "isResize":
        // 先对传入的参数进行结构，得到需要的参数信息
        const { width = "-1", height = "-1" } = parameter;

        // 只有当宽高存在时可以回调本函数，带入尺寸验证
        width !== "-1" &&
          (result = this.isError({ type: "isSize", parameter: width }));
        height !== "-1" &&
          (result = this.isError({ type: "isSize", parameter: height }));

        // 对传入的位置信息参数进行验证
        width === "-1" &&
          height === "-1" &&
          console.error(`请传入改变尺寸对应的width或者height`);
        break;

      default:
        break;
    }

    // 弹出最终的结果
    // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    return result;
  };
}
